/*
 *  Pedometer - Android App
 *  Copyright (C) 2009 Levente Bagi
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package name.bagi.levente.pedometer;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;

import com.codoon.gps.count.StepCounter;
import com.goodving.guli.model.DailyStats;
import com.goodving.guli.model.DailyStatsByDay;
import com.goodving.guli.utility.C1465g;
import com.goodving.guli.utility.LdlDate;

import java.util.ArrayList;

//import com.goodving.pedometer.StepCounter;

/**
 * Detects steps and notifies all listeners (that implement StepListener).
 * @author Levente Bagi
 * @todo REFACTOR: SensorListener is deprecated
 */
public class StepDetector implements SensorEventListener
{

    private final static String TAG = "StepDetector";
    private float   mLimit = 10;
    private float   mLastValues[] = new float[3*2];
    private float   mScale[] = new float[2];
    private float   mYOffset;

    private float   mLastDirections[] = new float[3*2];
    private float   mLastExtremes[][] = { new float[3*2], new float[3*2] };
    private float   mLastDiff[] = new float[3*2];
    private int     mLastMatch = -1;
    private int     stepDetectorAlgo = 0;
    private long    mLastStep = 0;
    private long    mCurStep = 0;
    private static LdlDate mLdlDate = LdlDate.m8856a();//TBD
    private static DailyStatsByDay mDailyStatsByDay = DailyStatsByDay.getInstance();
    private static DailyStats mDailyStats = mDailyStatsByDay.getDailyStatsByDay(mLdlDate);

    static {
    	System.loadLibrary("stlport_shared");
        System.loadLibrary("step-counter");
        // here we need to clear the counter at the very beginning
    }


    private ArrayList<StepListener> mStepListeners = new ArrayList<StepListener>();
    
    public StepDetector(int initStep) {
        int h = 480; // TODO: remove this constant
        mYOffset = h * 0.5f;
        mScale[0] = - (h * 0.5f * (1.0f / (SensorManager.STANDARD_GRAVITY * 2)));
        mScale[1] = - (h * 0.5f * (1.0f / (SensorManager.MAGNETIC_FIELD_EARTH_MAX)));
        StepCounter.writeStepCounter(initStep);
        mDailyStats.setGoalStep(C1465g.aQ);
    }
    
    public void setSensitivity(float sensitivity) {
        mLimit = sensitivity; // 1.97  2.96  4.44  6.66  10.00  15.00  22.50  33.75  50.62
    }
    
    public void addStepListener(StepListener sl) {
        mStepListeners.add(sl);
    }
    
    
    
    //public void onSensorChanged(int sensor, float[] values) {
    @SuppressWarnings("deprecation")
	public void onSensorChanged(SensorEvent event) {
        Sensor sensor = event.sensor; 
        synchronized (this) {
            if (sensor.getType() == Sensor.TYPE_ORIENTATION) {
            }
            else {
                int j = (sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? 1 : 0;
                if (j == 1) {
                    switch(stepDetectorAlgo){
                        case 0:
                        {
                            // update the motion
                            if(StepCounter.updateStepCounter(event.timestamp / 1000000, event.values[0], event.values[1], event.values[2]))
                            {
                            	// to check if the new Cur Step is not equal the last one
                            	// Yes, notify all listener
                            	// No, continue without notification
                            	if((mCurStep = StepCounter.readStepCounter()) > mLastStep)
                            	{

                                    for (StepListener stepListener : mStepListeners) {
                                        stepListener.onStep((int) (mCurStep));
                                    }
                                    mLastStep = mCurStep;
                            	} else if (mCurStep < mLastStep) {
                            		mCurStep = 0;
                            		mLastStep = 0;
                            		StepCounter.writeStepCounter(0);//TBD
                            	}
                            }
                            break;
                        }
                        
                        case 1:
                        {
                            float vSum = 0;
                            for (int i=0 ; i<3 ; i++) {
                                final float v = mYOffset + event.values[i] * mScale[j];
                                vSum += v;
                            }
                            Log.e(TAG, "Time:" + event.timestamp / 1000000 + " X:" + event.values[0] + " Y:" + event.values[1] + " Z:" + event.values[2]);
                            int k = 0;
                            float v = vSum / 3;

                            float direction = (v > mLastValues[k] ? 1 : (v < mLastValues[k] ? -1 : 0));
                            if (direction == - mLastDirections[k]) {
                                // Direction changed
                                int extType = (direction > 0 ? 0 : 1); // minumum or maximum?
                                mLastExtremes[extType][k] = mLastValues[k];
                                float diff = Math.abs(mLastExtremes[extType][k] - mLastExtremes[1 - extType][k]);

                                if (diff > mLimit) {

                                    boolean isAlmostAsLargeAsPrevious = diff > (mLastDiff[k]*2/3);
                                    boolean isPreviousLargeEnough = mLastDiff[k] > (diff/3);
                                    boolean isNotContra = (mLastMatch != 1 - extType);

                                    if (isAlmostAsLargeAsPrevious && isPreviousLargeEnough && isNotContra) {
                                        Log.i(TAG, "step");
                                        for (StepListener stepListener : mStepListeners) {
                                            stepListener.onStep((int)mCurStep);
                                        }
                                        mLastMatch = extType;
                                    }
                                    else {
                                        mLastMatch = -1;
                                    }
                                }
                                mLastDiff[k] = diff;
                            }
                            mLastDirections[k] = direction;
                            mLastValues[k] = v;
                            break;
                        }
                        
                        default:
                        	break;
                    }

                }
            }
        }
    }
    
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO Auto-generated method stub
    }
    
    public long getCurStepCounter()
    {
    	return mCurStep;
    }
}